﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace ChessSharp.BoardCode
{
    public delegate void GameBoardEventPasser(object sender, BoardEventArgs b);

    public partial class GameBoard : Control
    {
        float SquareSize;
        PointF StartingPosition;
        RectangleF[] BlackSquares;
        RectangleF[] WhiteSquares;
        RectangleF[] AllSquares;
        RectangleF[] AllowSquares;
        RectangleF MORect;
        RectangleF FirstClickRect;
        public event GameBoardEventPasser GameEvent;

        public GameBoard()
        {
            InitializeComponent();
            this.MORect.Height = 0;
            this.DoubleBuffered = true;
            this.AllowSquares = null;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            this.DrawBoard(e);
            this.DrawLegalMoves(e.Graphics);
            this.DrawClickRect(e.Graphics);
            this.DrawSelection(e.Graphics);
        }

        private void DrawBoard(PaintEventArgs e)
        {
            e.Graphics.DrawRectangles(new Pen(Color.Black, 2), this.AllSquares);
            e.Graphics.FillRectangles(new SolidBrush(Color.Black), this.BlackSquares);
            e.Graphics.FillRectangles(new SolidBrush(Color.White), this.WhiteSquares);
        }

        private void DrawLegalMoves(Graphics g)
        {
            if (this.AllowSquares != null)
                g.DrawRectangles(new Pen(Color.Green, 4), this.AllowSquares);
        }

        private void DrawClickRect(Graphics g)
        {
            if (!this.FirstClickRect.IsEmpty)
                g.DrawRectangle(new Pen(Color.Blue, 4), this.FirstClickRect.X, this.FirstClickRect.Y, this.FirstClickRect.Width, this.FirstClickRect.Height);
        }

        private void DrawSelection(Graphics g)
        {
            if (!this.MORect.IsEmpty)
            {
                g.DrawRectangle(new Pen(Color.Gold, 4), this.MORect.X, this.MORect.Y, this.MORect.Width, this.MORect.Height);
            }
        }

        //Recalculate square size and positoins based on resize then invalidate to force redraw
        private void GameBoard_Resize(object sender, EventArgs e)
        {
            //Calculate SquareSize And Starting Position
            this.SquareSize = ((this.Width < this.Height) ? ((float)this.Width) / 8 : ((float)this.Height) / 8);
            if (this.Width != this.Height)
            {
                this.StartingPosition = ((this.Width < this.Height) ? (new PointF(0, (float)(this.Height - this.Width) / 2)) : (new PointF((float)(this.Width - this.Height) / 2, 0)));
            }

            this.BlackSquares = this.GenerateRectangles(SquareSubset.Black);
            this.WhiteSquares = this.GenerateRectangles(SquareSubset.White);
            this.AllSquares = this.GenerateRectangles(SquareSubset.All);

            //Force Control to redraw
            this.Invalidate();
        }

        /// <summary>
        /// Generates Rectangles for the board
        /// </summary>
        /// <param name="Squares">Select Subset of rectangles to generate</param>
        /// <returns>Selected Rectangles</returns>
        private RectangleF[] GenerateRectangles(SquareSubset Squares)
        {
            List<RectangleF> RecsToReturn = new List<RectangleF>();
            int ICt = 0;
            int JCt = 0;

            for (float i = this.StartingPosition.X; i < (this.StartingPosition.X + (SquareSize * 8)); i += SquareSize)
            {
                for (float j = this.StartingPosition.Y; j < (this.StartingPosition.Y + (SquareSize * 8)); j += SquareSize)
                {
                    if (Squares == SquareSubset.All || (Squares == SquareSubset.White && IsWhite(ICt, JCt)) || (Squares == SquareSubset.Black && !IsWhite(ICt, JCt)))
                        RecsToReturn.Add(new RectangleF(i, j, SquareSize, SquareSize));

                    JCt++;
                }

                ICt++;
                JCt = 0;
            }

            return RecsToReturn.ToArray();
        }

        private bool isEven(int i)
        {
            return ((i % 2) == 0);
        }

        private bool IsWhite(int i, int j)
        {
            return (isEven(i) == isEven(j));
        }

        private enum SquareSubset
        {
            All,
            Black,
            White,
        }

        public RectangleF RectAtPoint(Point P)
        {
            List<RectangleF> Rects = new List<RectangleF>();
            Rects.AddRange(this.AllSquares);
            return Rects.Find(delegate(RectangleF R) { return R.Contains(P); });
        }

        private void GameBoard_MouseMove(object sender, MouseEventArgs e)
        {
            RectangleF temp = new RectangleF(this.MORect.X, this.MORect.Y, this.MORect.Width, this.MORect.Height);
            try
            {
                this.MORect = this.RectAtPoint(e.Location);
            }
            catch
            {
                this.MORect.Width = 0;
            }
            finally
            {
                if(this.MORect != temp)
                    this.Invalidate();
            }
        }

        private void GameBoard_MouseLeave(object sender, EventArgs e)
        {
            if (!this.MORect.IsEmpty)
            {
                this.MORect.Width = 0;
                this.Invalidate();
            }
        }

        private Point RealRectToTileCountRect(RectangleF R)
        {
            Point temp = new Point((int)((R.X - this.StartingPosition.X) / this.SquareSize), (int)((R.Y - this.StartingPosition.Y) / this.SquareSize));
            return temp;
        }

        private RectangleF[] PointsToRects(Point[] P)
        {
            List<RectangleF> Rects = new List<RectangleF>();

            foreach (Point p in P)
            {
                float x = p.X * this.SquareSize + this.StartingPosition.X + (this.SquareSize * 0.1f);
                float y = p.Y * this.SquareSize + + this.StartingPosition.Y + (this.SquareSize + 0.1f);

                Rects.Add(this.RectAtPoint(new Point((int)x, (int)y)));
            }

            if(Rects.Count > 0)
                return Rects.ToArray();

            return null;
        }

        private void GameBoard_MouseClick(object sender, MouseEventArgs e)
        {
            RectangleF temp = new RectangleF(this.FirstClickRect.X, this.FirstClickRect.Y, this.FirstClickRect.Width, this.FirstClickRect.Height);

            try
            {
                if (this.FirstClickRect.IsEmpty)//if this is the first click
                {
                    BoardEventArgs B = new BoardEventArgs(BoardEventType.TileSelect, this.RealRectToTileCountRect(this.RectAtPoint(e.Location)));
                    if(this.GameEvent != null)
                        this.GameEvent(this, B);

                    if (B.AllowAction)
                    {
                        this.FirstClickRect = this.RectAtPoint(e.Location);

                        if (B.LegalSquares != null)
                        {
                            this.AllowSquares = this.PointsToRects(B.LegalSquares);
                        }
                    }
                }
                else if (this.RectAtPoint(e.Location) == this.FirstClickRect)
                {
                    this.FirstClickRect.Width = 0;
                    this.AllowSquares = null;
                }
                else //if we are trying to make a move
                {
                    BoardEventArgs B = new BoardEventArgs(BoardEventType.MoveAttempt, this.RealRectToTileCountRect(this.RectAtPoint(e.Location)));
                    if (this.GameEvent != null)
                        this.GameEvent(this, B);

                    if (B.AllowAction)
                    {
                        this.FirstClickRect.Width = 0;
                        this.AllowSquares = null;
                    }
                }
            }
            catch
            {
                this.FirstClickRect.Width = 0;
            }
            finally
            {
                if (temp != this.FirstClickRect)
                    this.Invalidate();
            }
        }
    }
}
